﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GenericGA.extrandom;
using GenericGA.reader;

namespace GenericGA.generationmaker
{
    /// <summary>
    /// An individual in the population of the genetic algorithm.
    /// </summary>
    /// <typeparam name="P">The phenotype type, or type of the candidate solutions.</typeparam>
    public class Individual<P>
    {

        /// <summary>
        /// The binary chromosome which determines the phenotype of the individual.
        /// </summary>
        private readonly Chromosome chromosome;

        /// <summary>
        /// The binary chromosome which determines the phenotype of the individual.
        /// </summary>
        public List<ulong> Chromosome
        {
            get
            {
                return chromosome.Bits;
            }
        }

        /// <summary>
        /// The phenotype, or candidate solution itself.
        /// </summary>
        public P Phenotype { get; private set; }

        /// <summary>
        /// The cached fitness of the individual according to a particular fitness function. Since this value could change it should be refreshed with every generation. Starts as NaN and automatically gets set by the genetic algorithm object at the end of a new generation.
        /// </summary>
        public double CachedFitness { get; private set; }

        /// <summary>
        /// Constructor to create an individual from a chromosome.
        /// </summary>
        /// <param name="randGen">A random number generator.</param>
        /// <param name="phenotypeBuildingFunction">A phenotype building function which turns the chromosome into a phenotype or candidate solution.</param>
        /// <param name="chromosome">A binary chromosome which determines the phenotype.</param>
        public Individual(ExtRandom randGen, GA<P>.PhenotypeBuildingFunctionType phenotypeBuildingFunction, List<ulong> chromosome)
        {
            this.chromosome = new Chromosome(randGen, chromosome);
            this.Phenotype = phenotypeBuildingFunction(this.chromosome.GetReader());
            this.CachedFitness = double.NaN;
        }

        /// <summary>
        /// Refresh the cached fitness of this individual if it is going to be reused in the next generation.
        /// </summary>
        /// <param name="fitnessFunction">The fitness function.</param>
        public void RefreshFitness(GA<P>.FitnessFunctionType fitnessFunction)
        {
            CachedFitness = fitnessFunction(Phenotype);
        }

        /// <summary>
        /// Get the number of bits in the chromosome.
        /// </summary>
        /// <returns>The number of bits.</returns>
        public int ChromosomeLength()
        {
            return chromosome.BitLength();
        }

    }
}
